/*
 *  SFClib - Secure File Container Library
 *
 *  sfclib.c - Copyright(c) 2009 The SFCLib authors
 *
 *  The original author of this file is: Roberto Estrada Casarrubios.
 *
 *	This file is part of SFClib.
 *
 *  SFClib is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  SFClib is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with SFClib.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <openssl/aes.h>
#include "sfclib.h"

/* 1.0 SFC Magic number (Rebeca's Number) */
#define MAGIC_NUMBER 0xC46B7D80

typedef struct SFC_header
{
	/* Unique identifier for SFC Files */
	uint32 magic_number;

	/* Length in bytes of the I-List */
	uint64 ilist_length;

	/* Length in bytes of the data area */
	uint64 dataarea_length;

} SFC_header_t;

typedef struct SFC_entry
{
	/* Unique I-List entry identifier */
	uint32 id;

	/* Zero if this entry is a file, One if a directory*/
	uint32 type;

	/* The filename, allows 256 character long */
	char name [256];

	/* The start position of a file in the data area of an SFC */
	uint64 startpos;

	/* The length in bytes of the entry in the data area */
	uint64 length;

	/* This is the Entry id of this I-list entry parent */
	uint32 parent_id;

	/* This is the CRC32 of the data, it allows checking if the contents are corrupt */
	uint32 crc32;

} SFC_entry_t;

/* This is the id which will be assigned to the next I-List entry */
uint32 current_id;

/* Descriptor for the current working directory */
uint32 cwd;

/* This number tells how many entries are files */
unsigned int numfiles;

/* This number tells how many entries are directories */
unsigned int numdirs;

/* This is the currently open SFC */
FILE *currentSFC;

/* Pointer to a temporary file where the data area is built */
FILE *tmpDataArea;

/* Pointer to the file list of the I-list */
SFC_entry_t *fileList;

/* Pointer to the directory list of the I-list */
SFC_entry_t *dirList;

/* The keys for the current SFC file */
AES_KEY encryptKey;
AES_KEY decryptKey;

#ifdef _WINDLL
/*
 * Windows DLL Entry point, only returns true
 */
BOOL WINAPI DllMain ( HANDLE hModule, DWORD dwFunction, LPVOID lpNot)
{
	return TRUE;
}
#endif

/*
 * @fn	SFCAPI int createSFC(char* name, char* path)
 * 
 * @brief	Creates a new SFC file with the given name in the specified
 *			path.
 * 
 * @author	Roberto Estrada Casarrubios
 * 
 * @param	fullpath   The full pathname where the file will be created.
 * @param	key		The key which decrypts the contents of the file.
 * 
 * @return	0 if succesful, -1 if any error occured. 
 */
int createSFC(char* fullpath,char* key)
{
	SFC_entry_t *root;

	/* Generating AES Key */
	if(AES_set_encrypt_key(key,128,&encryptKey) < 0)
	{
		return -1;
	}
	
	if(AES_set_decrypt_key(key,128,&decryptKey) < 0)
	{
		return -1;
	}

	/* Sets to zero the current entry id */
	current_id = 0;

	/* Initializing values */
	numfiles = 0;
	numdirs = 1;

	/* Creating the new SFC file at the specified path */
	currentSFC = fopen(fullpath,"wb+");
	/* Creating a tempfile for data area build */
	tmpDataArea = tmpfile();

	/* Initializing lists */
	dirList = (SFC_entry_t*)malloc(sizeof(SFC_entry_t));
	fileList = (SFC_entry_t*)malloc(sizeof(SFC_entry_t));

	/* Checking if malloc failed */
	if(dirList == NULL || fileList == NULL)
	{
		/* Doh! malloc failed, we can't create the SFC! */
		return -1;
	}

	/* Initializing the root directory in the I-list */
	root = &dirList[0];
	root->id = current_id;
	root->type = 1;
	strcpy(root->name,"/");
	root->startpos = 0;
	root->length = 0;
	root->parent_id = root->id;
	root->crc32 = 0;

	/* Setting the cwd to the root */
	cwd = root->id;

	/* Incrementing counters */
	current_id++;
	
	/* Exiting gracefully */
	return 0;
}

/*
 * @fn	uint32 sf_creat(char* name)
 * 
 * @brief	Creates a new file inside the SFC File 
 * 
 * @author	Roberto Estrada Casarrubios
 * 
 * @param	name The name of the file to create. 
 * 
 * @return	The unique 64-bit file descriptor, -1 if any error occured. 
 */
uint32 sf_creat(char* name)
{
	/* Pointer to the new entry */
	SFC_entry_t *file;
	
	/* Temporary pointer for the file list to realloc */
	SFC_entry_t *tmp;
	
	/* Auxiliary counter for loops */
	uint32 i;

	numfiles++;	
	
	if(numfiles == 1)
	{
		file = &fileList[0];
	}
	else
	{
		/* Growing the file list */
		tmp = (SFC_entry_t*)realloc(fileList,numfiles*sizeof(SFC_entry_t));

		/* If realloc went bad this avoids losing the pointer to the file list */
		if(tmp == NULL)
		{
			/* Realloc went bad, but at least we haven't lost the pointer to the file list */
			return -1;
		}
		else
		{
			fileList = tmp;
			file = &fileList[numfiles - 1];
		}
	}

	/* This avoids overflow on the entry name */
	if(strlen(name) > 256)
	{
		/* Overflow! exiting */
		return -1;
	}

	/* Checking if exists another file with the same name under the same directory */	
	for(i = 0;i<numfiles;i++)
	{
		/* This may speed up the safety check */
		if(fileList[i].parent_id == cwd)
		{
			if(strcmp(name,fileList[i].name) == 0)
			{
				/* Another file with the same name exists in the current directory!! */
				return -1;
			}
		}
	}

	/* Now we're in business! Let's fill the data entry! */
	file->id = current_id;
	file->type = 0;
	strcpy(file->name,name);
	file->parent_id = cwd;
	
	/* The rest of the data will be filled when the file is fully written
	to the data area. So we have finished working creating the entry. */

	current_id++;

	return file->id;
}

/*
 * @fn	int libdestroy()
 * 
 * @brief	Call to end the library usage
 * 
 * @author	Roberto Estrada Casarrubios
 * @date	18/06/2009
 * 
 * @return	0 if succesful, -1 if any error occured.
 */

int libdestroy()
{
	/* Closes the actual file */
	if(fclose(currentSFC) < 0)
	{
		return -1;
	}
	
	/* Removes the temporary data area file */
	if(fclose(tmpDataArea))
	{
		return -1;
	}
	
	/* Frees the file list from memory */
	free(fileList);
	
	/* Wipes the directory list from memory */
	free(dirList);

	/* Successful destruction! */
	return 0;
}